#ifndef ANDRES_PARALLEL_UTILITY_HPP_
#define ANDRES_PARALLEL_UTILITY_HPP_

#include <thread>
#include <mutex>
#include <vector>
#include <stdexcept> 

struct LOOP_PARALISM {
        explicit LOOP_PARALISM(size_t start, size_t end) : start_(start), end_(end) {
            numthreads_ = (std::thread::hardware_concurrency()>0) ? std::thread::hardware_concurrency() : 1; 
            chunk_ = (end_ - start_ + (numthreads_ - 1)) / numthreads_;
            works_ = std::vector<std::thread>(numthreads_);
        }
        explicit LOOP_PARALISM() {
            numthreads_ = (std::thread::hardware_concurrency()>0)? std::thread::hardware_concurrency(): 1; 
            chunk_ = 1;
            start_ = 0; 
            end_ = numthreads_;
            works_ = std::vector<std::thread>(numthreads_);
        }
        ~LOOP_PARALISM() {
            for (auto &&t: works_) {
                if (t.joinable()) { t.join(); }
            }
        }
        LOOP_PARALISM(const LOOP_PARALISM&)  = delete;
        LOOP_PARALISM(const LOOP_PARALISM&&) = delete;

        static size_t get_chunk(size_t num) {
            int numthreads_ = (std::thread::hardware_concurrency()>0) ? std::thread::hardware_concurrency() : 1; 
            return (num + (numthreads_ - 1)) / numthreads_;
        }

        void sub_index() {
            sub_s_ = start_ + thread_index_ * chunk_;
            sub_e_ = sub_s_ + chunk_;
            if (sub_e_ > end_) { sub_e_ = end_; }
        }

        size_t numthreads_;
        size_t thread_index_;
        size_t start_;
        size_t end_;
        size_t chunk_;
        std::vector<std::thread> works_;
        std::mutex m_;
        size_t sub_s_;
        size_t sub_e_;
};

#endif // PARALLEL_UTILITY_HPP_
